#include "StdAfx.h"

#include <QVector3D>

#include <Geom2d_TrimmedCurve.hxx>
#include <Geom2dAPI_InterCurveCurve.hxx>
#include <Geom2dAPI_ProjectPointOnCurve.hxx>

#include "Arc2D.h"
#include "Trim2DHandler.h"
#include "SelectHandler3D.h"
#include "Qt3DPicker.h"
#include "CmdManager.h"
#include "QtOSGWidget.h"
#include "OsgUtil.h"
#include "Scene.h"
#include "Operation/OpReplace.h"
#include "OcctShapeProp.h"

bool isCircle(double startAngle, double endAngle);

bool operator<(const SectionParam & x1, const SectionParam & x2)
{
	if (x1.sectionNo < x2.sectionNo)
		return true;
	if (x1.sectionNo > x2.sectionNo)
		return false;
	return 1E-4 < x2.param - x1.param;
}

std::vector<SectionParam> intersectForTrim(const std::vector<Handle(Geom2d_Curve)>& curves, const std::vector<Handle(Geom2d_Curve)>& boundaries)
{
	std::vector<SectionParam> result;
	Geom2dAPI_InterCurveCurve inter;
	for (int curveNo = 0; curveNo < curves.size(); ++curveNo)
	{
		const auto& curve = curves[curveNo];
		for (const auto& bound : boundaries)
		{
			inter.Init(curve, bound);
			for (int i = 1; i <= inter.NbPoints(); ++i)
			{
				auto pt = inter.Point(i);
				double param;
				bool success = getParam(curve, true, pt, param);
				result.push_back({ curveNo, param });
			}
		}
	}
	return result;
}

bool getSectionParam(const std::vector<Handle(Geom2d_Curve)>& curves, const gp_Pnt2d & point, SectionParam & result)
{
	double dist = DBL_MAX;
	bool success = false;
	Geom2dAPI_ProjectPointOnCurve proj;// (point, basisCurve(curve));
	for (int i = 0; i < curves.size(); ++i)
	{
		auto curve = curves[i];
		proj.Init(point, curve);
		if (proj.NbPoints() > 0)
		{
			double ld = proj.LowerDistance();
			if (ld < dist)
			{
				success = true;
				dist = ld;
				double param = proj.LowerDistanceParameter();
				if (basisCurve(curve)->IsPeriodic())
				{
					double fp = curve->FirstParameter();
					double lp = curve->LastParameter();
					double period = curve->Period();
					while (param > lp)
					{
						param -= period;
					}
					while (param < fp)
					{
						param += period;
					}
				}
				result.sectionNo = i;
				result.param = param;
			}
		}
	}
	return success;
}

void getStartEndParam(const std::vector<Handle(Geom2d_Curve)>& curves, SectionParam & from, SectionParam & to)
{
	if (curves.empty())
		return;
	from.sectionNo = 0;
	to.sectionNo = curves.size() - 1;
	from.param = curves[0]->FirstParameter();
	to.param = curves[to.sectionNo]->LastParameter();
}

Trim2DHandler::Trim2DHandler()
{

}

void Trim2DHandler::input(const QVariant& data, const QString& rawInput, CommandSource cs)
{
	//Esc is pressed
	auto selector = qobject_cast<SelectHandler3D*>(getSelectHandler());
	if (data.type() == QVariant::Bool)
	{
		bool esc = !data.toBool();
		if (esc)
		{
			selector->clearSelection();
			clearInput();
			emit finished();
			return;
		}
	}

	if(status() == 0)
	{
		if (selector)
		{
			selector->clearSelection();
			selector->beginSelect(D2CurveMask);
			prompt(1, tr("选择二维边界曲线: "));
		}
		else
		{
			emit finished();
		}
		return;
	}

	if(status() == 1)
	{
		if (selector && selector->done())
		{
			auto selections = selector->selectionSet();
			if (selections.size() > 0)
			{
				std::vector<std::shared_ptr<Ent>> origins;
				std::vector<std::shared_ptr<Ent>> explodes;
				for (auto selection : selections)
				{
					auto ent = std::dynamic_pointer_cast<Ent2DCurve>(selection);
					if (ent)
					{
						auto parent = ent->getParent();
						UCS* ucs = dynamic_cast<UCS*>(parent);

						if (ucs)
						{
							if (m_ucs == nullptr)
								m_ucs = ucs;
							if (ucs == m_ucs)
							{
								m_TrimBorders.insert(ent);
							}
							else
							{
								remark(tr("请选择同一平面的二维图形，已忽略"));
							}
						}
						else
						{
							remark(tr("请选择二维图形"));
							emit finished();
							return;
						}
					}
				}
				selector->clearSelection();
				if (m_TrimBorders.empty())
				{
					emit finished();
					return;
				}
				else
				{
					selector->beginSelect(D2CurveMask, 1);
					prompt(2, tr("选择二维待修剪曲线: "));
				}
			}
		}
		return;
	}

	if (status() == 2)
	{
		if (selector->done())
		{
			std::set<std::shared_ptr<INode>> nodes = selector->selectionSet();
			selector->clearSelection();
			if (nodes.empty())
			{
				emit finished();
				return;
			}
			std::shared_ptr<INode> node = *nodes.begin();
			auto curve = std::dynamic_pointer_cast<Ent2DCurve>(node);
			if (curve)
			{
				auto parent = curve->getParent();
				UCS* ucs = dynamic_cast<UCS*>(parent);

				if (ucs)
				{
					if (ucs == m_ucs)
					{
						if (m_TrimBorders.find(curve) != m_TrimBorders.end())
						{
							remark(tr("对象属于边界，不能修剪"));
							selector->beginSelect(D2CurveMask, 1);
							return;
						}
						std::vector<Handle(Geom2d_Curve)> curve2ds = curve->getCurve2d();
						if (curve2ds.empty())
							return;
						std::vector<Handle(Geom2d_Curve)> boundaries;
						for (const auto& border : m_TrimBorders)
						{
							auto tmp = border->getCurve2d();
							boundaries.insert(boundaries.end(), tmp.begin(), tmp.end());
						}
						auto widget3D = getWidget();
						osg::Vec2d cursor = widget3D->getCursorPoint();
						osg::Camera* camera = widget3D->getCamera();
						if (!camera)
						{
							emit finished();
							return;
						}
						osg::Matrix VPW, invVPW;
						bool success = getCameraViewProjectionWindowMatrix(camera, VPW, invVPW);
						if (!success)
						{
							emit finished();
							return;
						}
						osg::Vec3d worldPt(cursor.x(), cursor.y(), 0);
						worldPt = worldPt * invVPW;
						osg::Vec3d ucsPt;
						getViewPointOnPlane(*m_ucs, worldPt, ucsPt);
						gp_Pnt2d ucsPnt(ucsPt.x(), ucsPt.y());
						SectionParam hitPointInfo;
						success = getSectionParam(curve2ds, ucsPnt, hitPointInfo);
						if (!success)
						{
							remark(tr("没有找到点附近的曲线"));
							selector->beginSelect(D2CurveMask, 1);
							return;
						}
		
						auto xinfos = intersectForTrim(curve2ds, boundaries);
						if (xinfos.empty())
						{
							remark(tr("没有找到任何交点"));
							selector->beginSelect(D2CurveMask, 1);
							return;
						}
						bool trim = false;
						SectionParam from, to;
						getStartEndParam(curve2ds, from, to);
						auto arc2D = std::dynamic_pointer_cast<Arc2D>(curve);
						if (arc2D&&isCircle(arc2D->startAngle(), arc2D->endAngle()))
						{
							to.param += 2 * M_PI;
							from.param -= 2 * M_PI;
							for (auto& xinfo : xinfos)
							{
								if (xinfo < hitPointInfo)
								{
									if (from < xinfo)
									{
										from = xinfo;
										trim = true;
									}
									xinfo.param += 2 * M_PI;
									if (xinfo < to)
									{
										to = xinfo;
										trim = true;
									}
								}
								else
								{
									if (xinfo < to)
									{
										to = xinfo;
										trim = true;
									}
									xinfo.param -= 2 * M_PI;
									if (from < xinfo)
									{
										from = xinfo;
										trim = true;
									}
								}
							}
						
						}
						else
						{
							for (const auto& xinfo : xinfos)
							{
								if (xinfo < hitPointInfo)
								{
									if (from < xinfo)
									{
										from = xinfo;
										trim = true;
									}
								}
								else
								{
									if (xinfo < to)
									{
										to = xinfo;
										trim = true;
									}
								}
							}
						}
						if (!trim)
						{
							remark(tr("没有找到修剪段"));
							selector->beginSelect(D2CurveMask, 1);
							return;
						}

						std::vector<std::shared_ptr<Ent2DCurve>> newCurves = curve->trim(from, to);
						if (newCurves.empty())
						{
							remark(tr("无法修剪此对象"));
							selector->beginSelect(D2CurveMask, 1);
							return;
						}
						std::vector<std::shared_ptr<Ent>> oldEnts;
						oldEnts.push_back(curve);
						std::vector<std::shared_ptr<Ent>> newEnts;
						for (const auto& curve : newCurves)
							newEnts.push_back(curve);
						auto scene = getScene();
						auto undoStack = scene->undoStack();
						undoStack->push(new OpReplace(oldEnts, newEnts));

						selector->beginSelect(D2CurveMask, 1);
					}
					else
					{
						remark(tr("请选择同一平面的二维图形，已忽略"));
						selector->beginSelect(D2CurveMask, 1);
					}
				}
				else
				{
					remark(tr("请选择二维图形，已忽略"));
					selector->beginSelect(D2CurveMask, 1);
					return;
				}
			}

		}
	}
}
